import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'
import { plainToInstance, instanceToPlain } from 'class-transformer'
import { Between, EntityManager, Repository } from 'typeorm'
import * as fs from 'fs'
import * as uuid from 'uuid'
import path from 'path'
import mime from 'mime-types'

import { ResultData } from '../../common/utils/result'
import { AppHttpCode } from '../../common/enums/code.enum'

import { OssEntity } from './oss.entity'
import { FindOssDto } from './dto/find-oss.dto'
import {Response} from "express";
import {map} from "rxjs/operators";
import {HttpService} from "@nestjs/axios";
import {QiniuService} from "./qiniu.service";
import {getFileRealName} from "../../utils/common";
import {OssController} from "./oss.controller";

@Injectable()
export class OssService {
  // 当前项目 地址 如： **/**/dist
  private readonly productLocation = process.cwd()
  // 文件上传存储路径
  private basePath = ''
  private useRemoteLocation = false
  // res:Response = null
  constructor(
    private readonly config: ConfigService,
    @InjectRepository(OssEntity)
    private readonly ossRepo: Repository<OssEntity>,
    @InjectEntityManager()
    private readonly ossManager: EntityManager,
    private readonly httpService: HttpService,
    private readonly qiniuService: QiniuService
  ) {
    const configLocation = this.config.get<string>('app.file.location') || '../upload'
    this.basePath = path.normalize(
      path.isAbsolute(configLocation) ? `${configLocation}` : path.join(this.productLocation, `${configLocation}`),
    )
    this.useRemoteLocation = this.config.get('app.file.remoteLocation')
    console.log(this.basePath)
    // 检测文件存储 文件夹是否存在
    // if (!fs.existsSync(this.basePath)) {
    //   fs.mkdirSync(this.basePath)
    // }
    if(!this.useRemoteLocation){
      const afterPath = this.useRemoteLocation? configLocation:this.basePath
      try {
        // fs.accessSync(this.basePath, fs.constants.F_OK | fs.constants.W_OK)
        if(!this.useRemoteLocation){
          fs.accessSync(this.basePath, fs.constants.W_OK)
        }
      } catch (error) {
        throw new Error(
            `文件存储路径配置 app.file.location = ${afterPath} (完整路径： ${afterPath} ) 无写入权限`,
        )
      }
    }

  }

  // 批量上传文件
  async uploadFile(
    file: Express.Multer.File,
    business: string,
    user: { id: string; account: string },
    req: any
  ): Promise<ResultData> {
    // const domain = this.config.get<string>('app.file.domain')
    // 如果使用OSS存储
    if(this.useRemoteLocation){
      // 调用七牛云存储
      // const file = (req.body)
      const originalName = getFileRealName(file.originalname)
      const uploadRes = await this.qiniuService.upload(originalName,file.buffer);
      if(uploadRes){
        const ossFile = {
          userId: user?.id || '999',
          userAccount: user?.account || 'admin',
          remark: '上传至七牛云存储',
          size: file.size,
          type: file.mimetype,
          location: '',
          business: '',
          url: uploadRes?.url as string,
          fileName: uploadRes?.key as string
        }
        const instance = plainToInstance(OssEntity,ossFile)
        const result = await this.ossManager.transaction(async (transactionalEntityManager) => {
          return await transactionalEntityManager.save<OssEntity>(instance)
        })
        return ResultData.ok([result],'上传成功')
      }

    }else{
      try {
        // 不使用oss存储则使用本地存储
        // 重新命名文件， uuid, 根据 mimeType 决定 文件扩展名， 直接拿后缀名不可靠
        const newFileName = `${uuid.v4().replace(/-/g, '')}.${mime.extension(file.mimetype)}`
        // const newFileName = `${uuid.v4().replace(/-/g, '')}.${file.originalname.split('.').pop().toLowerCase()}`
        // 文件存储路径
        const fileLocation = path.normalize(path.join(this.basePath, newFileName))
        // fs 创建文件写入流
        const writeFile = fs.createWriteStream(fileLocation)
        // 写入文件
        writeFile.write(file.buffer)
        // 千万别忘记了 关闭流
        writeFile.close()
        const ossFile = {
          url: `${this.config.get<string>('app.file.domain')}${
              this.config.get<string>('app.file.serveRoot') || ''
          }/${newFileName}`,
          size: file.size,
          type: file.mimetype,
          location: fileLocation,
          business: business || '',
          userId: user.id,
          userAccount: user.account,
          remark: '上传至本地存储',
          fileName: newFileName
        }
        const instance = plainToInstance(OssEntity, ossFile)
        const result = await this.ossManager.transaction(async (transactionalEntityManager) => {
          return await transactionalEntityManager.save<OssEntity>(instance)
        })
        if (!result) {
          return ResultData.fail(AppHttpCode.SERVICE_ERROR, '文件存储失败，请稍后重新上传')
        }
        console.log(instanceToPlain(result))
        return ResultData.ok(instanceToPlain(result),'上传成功')
      }catch (e) {
        return ResultData.fail(AppHttpCode.SERVICE_ERROR, '文件存储失败，请稍后重新上传')
      }

    }
  }
  async getUploadInfo(){
    const token =  this.qiniuService.getUploadToken();
    const domain = this.qiniuService.getConfig().domain
    return ResultData.ok({
      token,
      domain,
    },'获取成功')
  }
  async findList(search: FindOssDto): Promise<ResultData> {
    const { size, page, startDay, endDay } = search
    const where = startDay && endDay ? { createDate: Between(`${startDay} 00:00:00`, `${endDay} 23:59:59`) } : {}
    const res = await this.ossRepo.createQueryBuilder('oss').distinctOn(['oss.url'])
        .where(where).orderBy('oss.createDate', 'DESC').skip(size * (page - 1) || 0).take(size).getManyAndCount()
    return ResultData.ok({ list: instanceToPlain(res[0]), total: res[1] })
  }
}
